package plus.decl;

import plus.B;
import plus.DynamicExtent;
import plus.Function;
import plus.Gripe;
import plus.S_Function;
import plus.Symbol;
import plus.Type;
import plus.VScope;
import plus.exp.Exp;
import plus.run.Emit;
import plus.run.VM;

public class D_Function extends Declaration implements Function {
	final Symbol<Function> sym;
	final Signature formalSpecs;
	final TypeSpec rangeSpec;
	final Subroutine sub;
	final boolean isVoid;
	private final S_Function arrow;
	final private int arity;
	
	static D_Function current;
	
	@Override public int arity() { return arity; }
	
	public D_Function(
			int lineNr,
			boolean isVoid,
			String name,
			Signature formalSpecs,
			TypeSpec rangeSpec,
			Subroutine sub) {
		sym = new Symbol<Function>(name, lineNr, this);
		this.isVoid = isVoid;
		this.formalSpecs = formalSpecs;
		this.rangeSpec = rangeSpec;
		this.sub = sub;
		arity = formalSpecs.parameterCount();
		arrow = new S_Function(arity);
	}
	private DynamicExtent dynex;
	public void threadScope(int lexLevel) {
		// At this point the surrounding type environment has been built,
		// so we can interpret the signature and range meaningfully.
//		assert DynamicExtent.cur != null;
		dynex = new DynamicExtent(lexLevel);
		VScope.enterProc(dynex);
		// Figure the range:
		if (isVoid) Type.unify(arrow.range, B._void);
		else {
			if (null != rangeSpec) {
				Type.unify(arrow.range, rangeSpec.interpret());
			}
			dynex.setRange(arrow.range);
		}
		// Figure the domain:
		Gripe.context = this;
		Gripe.lineNr = sym.lineNr;
		formalSpecs.enterParams(arrow.domain);
		sub.threadScope(dynex);
		VScope.exit();
	}

	@Override public S_Function arrow() { return arrow; }

	@Override public void compileCall(Exp[] es) {
		if (!isVoid) Emit.asm(VM.NUL); // Room for return value
		// Push from right to left.
		int i = es.length;
		while (i-->0) es[i].compilePush();
		Emit.staticPtr(dynex.lexLevel-1);
		sub.entryPoint.JSR();
		// Rely on caller to clean up and leave stack right.
		// And by the way, we now compile by need.
		sub.scheduleCompile();
	}
}
